package middleware

import (
	_ "embed"
	"errors"
	"github.com/gin-gonic/gin"
	log "github.com/sirupsen/logrus"
	"github.com/storyicon/grbac"
	"github.com/storyicon/grbac/pkg/meta"
	"gopkg.in/yaml.v3"
	"net/http"
	"net/url"
	"simple-app/lib/jwt"
	"strings"
)

//go:embed auth_rbac.yml
var embedRules []byte

// GinAuthorizationMiddleAware 登录验证和rbac中间件
func GinAuthorizationMiddleAware() gin.HandlerFunc {
	rules := make(meta.Rules, 0)
	// 读取配置文件
	if err := yaml.Unmarshal(embedRules, &rules); err != nil {
		log.Fatal(err)
	}
	// 加载路由规则
	ctrl, err := grbac.New(grbac.WithRules(rules))
	if err != nil {
		log.Fatal(err)
	}
	return func(c *gin.Context) {
		rc, err := getBaseClaims(c)
		if err != nil {
			c.Status(http.StatusUnauthorized)
			c.Abort()
			return
		}
		state, err := ctrl.IsRequestGranted(c.Request, rc.Role)
		if err != nil {
			log.Error(err)
			c.Abort()
			return
		}
		if !state.IsGranted() {
			c.Status(http.StatusForbidden)
			c.Abort()
			return
		}
		c.Set("uid", rc.UserID)
	}
}

// getBaseClaims 有 cookie 从 cookie 中获取用户信息，没有 cookie 从 token 中获取
func getBaseClaims(c *gin.Context) (claims *jwt.CustomClaims, err error) {
	claims, err = getBaseClaimsCookie(c.Request)
	if err == nil {
		c.Set("claims", claims)
		c.Next()
		return
	}
	claims, err = getBaseClaimsToken(c.Request)
	if err == nil {
		c.Set("claims", claims)
		c.Next()
		return
	}
	return claims, errors.New("未认证的账户")
}

func getBaseClaimsToken(req *http.Request) (claims *jwt.CustomClaims, err error) {
	var (
		token string
	)
	token = req.Header.Get(jwt.Token)
	if token == "" {
		return claims, jwt.TokenMalformed
	}
	if strings.HasPrefix(token, jwt.TokenPre) {
		token = token[len(jwt.TokenPre):]
	}
	j := jwt.NewJWT()
	// parseToken 解析token包含的信息
	claims, err = j.ParseToken(token)
	return claims, err
}

func getBaseClaimsCookie(req *http.Request) (claims *jwt.CustomClaims, err error) {
	var (
		cookies []*http.Cookie
		cv      string
		//ok      bool
	)

	cookies = req.Cookies()
	for i := range cookies {
		if cookies[i].Name == jwt.CookieKey {
			if cookies[i].Value != "" {
				cv, err = url.QueryUnescape(cookies[i].Value)
				if err != nil {
					log.Error(err)
				}
			}
		}
	}
	if cv == "" {
		return claims, errors.New("无效 cookie")
	}
	//if claims, ok = db.CacheForCookie[cv]; !ok {
	//	return claims, errors.New("无效 cookie")
	//}
	return claims, nil
}
